package com.whli.autumn.mall.wms.manage;

import com.whli.autumn.core.exception.BusinessException;
import com.whli.autumn.core.extend.valid.ValidateUtils;
import com.whli.autumn.mall.wms.domain.dto.StockActionDTO;
import com.whli.autumn.mall.wms.domain.dto.StockOccupiedDTO;
import com.whli.autumn.mall.wms.domain.dto.convert.StockActionConvert;
import com.whli.autumn.mall.wms.domain.dto.stock.StockQueryDTO;
import com.whli.autumn.mall.wms.domain.dto.stockdtl.StockDtlQueryDTO;
import com.whli.autumn.mall.wms.domain.entity.Stock;
import com.whli.autumn.mall.wms.domain.entity.StockDtl;
import com.whli.autumn.mall.wms.domain.entity.StockLog;
import com.whli.autumn.mall.wms.domain.vo.stock.StockVO;
import com.whli.autumn.mall.wms.domain.vo.stockdtl.StockDtlVO;
import com.whli.autumn.mall.wms.enums.QualityEnum;
import com.whli.autumn.mall.wms.enums.StockActionEnum;
import com.whli.autumn.mall.wms.service.IStockDtlService;
import com.whli.autumn.mall.wms.service.IStockLogService;
import com.whli.autumn.mall.wms.service.IStockService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.List;

/**
 * <p>库存操作类</p>
 *
 * @author whli
 * @version 1.0.0
 * @since 2023/5/1 10:44
 */
@Component
public class StockActionManage {

    /** 默认仓库分类 */
    public static final String DEFAULT_WAREHOUSE_CATEGORY = "0";
    /** 默认批次号 */
    private static final String DEFAULT_BATCH = "00000000";

    @Autowired
    private IStockService stockService;
    @Autowired
    private IStockDtlService stockDtlService;
    @Autowired
    private IStockLogService stockLogService;

    /**
     * <p>入库</p>
     *
     * @author whli
     * @since 2023/5/1 11:24
     * @param entity 参数
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public synchronized int inStock(StockActionDTO entity) {
        if (entity == null) {
            throw new BusinessException("入库数据不能为空");
        }

        //默认值
        if (StringUtils.isEmpty(entity.getBatchNo())) {
            entity.setBatchNo(DEFAULT_BATCH);
        }
        if (StringUtils.isEmpty(entity.getSnNo())) {
            entity.setSnNo(DEFAULT_BATCH);
        }
        entity.setActionType(StockActionEnum.IN_STOCK.getType());

        //验证
        this.validParams(entity);

        //查询主库存
        List<StockVO> stocks = stockService.findByWarehouse(StockActionConvert.INSTANCE.inToQuery(entity));

        //创建/修改主库存
        Stock stock = StockActionConvert.INSTANCE.inToDomain(entity);
        String stockId = "";
        if (CollectionUtils.isEmpty(stocks)) {
            stockId = this.createStock(stock);
        } else {
            StockVO vo = stocks.get(0);
            stockId = vo.getId();

            stock.setId(vo.getId());
            BigDecimal stockQty = vo.getStockQty().add(entity.getStockQty()).setScale(2, BigDecimal.ROUND_HALF_UP);
            stock.setStockQty(stockQty);
            stockService.update(stock);
        }

        //查询库存明细
        StockDtlQueryDTO stockDtlQuery = StockActionConvert.INSTANCE.inToDtlQuery(entity);
        List<StockDtlVO> stockDtls = stockDtlService.findByWarehouseAndProduct(stockDtlQuery);

        //创建/更新库存明细
        StockDtl stockDtl = StockActionConvert.INSTANCE.actionToDtlDomain(entity);
        if (CollectionUtils.isEmpty(stockDtls)) {
            this.createStockDtl(stockId, stockDtl);
        } else {
            StockDtlVO vo = stockDtls.get(0);

            stockDtl.setId(vo.getId());
            BigDecimal stockQty = vo.getStockQty().add(entity.getStockQty()).setScale(2, BigDecimal.ROUND_HALF_UP);
            stockDtl.setStockQty(stockQty);
            stockDtlService.update(stockDtl);
        }

        //插入库存日志
        this.createStockLog(entity);
        return 1;
    }

    /**
     * <p>出库</p>
     *
     * @author whli
     * @since 2023/5/1 11:24
     * @param entity 参数
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int outStockAndNotVerifyOccupancy(StockActionDTO entity) {
        return this.outStock(entity, false);
    }

    /**
     * <p>出库并占用</p>
     *
     * @author whli
     * @since 2023/5/1 11:24
     * @param entity 参数
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int outStockAndVerifyOccupancy(StockActionDTO entity) {
        return this.outStock(entity, true);
    }

    /**
     * <p>占用库存</p>
     *
     * @author whli
     * @since 2023/5/1 14:43

     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public synchronized int occupiedStockQty(StockOccupiedDTO entity) {
        if (entity == null) {
            throw new BusinessException("占用库存数据不能为空");
        }
        //检验参数
        ValidateUtils.validate(entity);

        //验证主库存
        StockQueryDTO stockQuery = new StockQueryDTO();
        BeanUtils.copyProperties(entity, stockQuery);
        StockVO stock = this.validStockQty(stockQuery, entity.getStockQty());


        //验证库存明细
        StockDtlQueryDTO stockDtlQuery = new StockDtlQueryDTO();
        BeanUtils.copyProperties(entity, stockDtlQuery);
        StockDtlVO stockDtl = this.validStockDtlQty(stockDtlQuery, entity.getStockQty());

        //修改主库存及库存明细占用
        stockService.changeOccupiedQty(stock.getId(), entity.getStockQty());
        stockDtlService.changeOccupiedQty(stockDtl.getId(), entity.getStockQty());

        return 1;
    }

    /**
     * <p>取消占用</p>
     *
     * @author whli
     * @since 2023/5/1 14:43

     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public synchronized int cancelOccupiedStockQty(StockOccupiedDTO entity) {
        if (entity == null) {
            throw new BusinessException("取消占用库存数据不能为空");
        }
        //检验参数
        ValidateUtils.validate(entity);

        //验证主库存占用
        StockQueryDTO stockQuery = new StockQueryDTO();
        BeanUtils.copyProperties(entity, stockQuery);
        StockVO stock = this.validOccupiedQty(stockQuery, entity.getStockQty());


        //验证库存明细占用
        StockDtlQueryDTO stockDtlQuery = new StockDtlQueryDTO();
        BeanUtils.copyProperties(entity, stockDtlQuery);
        StockDtlVO stockDtl = this.validOccupiedDtlQty(stockDtlQuery, entity.getStockQty());

        //修改主库存及库存明细占用
        BigDecimal stockQty = entity.getStockQty().multiply(BigDecimal.valueOf(-1));
        stockService.changeOccupiedQty(stock.getId(), stockQty);
        stockDtlService.changeOccupiedQty(stockDtl.getId(), stockQty);

        return 1;
    }

    /**
     * <p>验证库存</p>
     *
     * @author whli
     * @since 2023/5/1 15:01
     * @param entity
     * @return
     */
    private StockVO validStockQty(StockQueryDTO entity, BigDecimal stockQty) {
        List<StockVO> stocks = stockService.findByWarehouse(entity);
        if (CollectionUtils.isEmpty(stocks)) {
            throw new BusinessException("库存不足，请补足后操作");
        }
        StockVO stock = stocks.get(0);
        if (stockQty.compareTo(stock.getAvailableStock()) > 0) {
            BigDecimal diffStockQty = stockQty.subtract(stock.getAvailableStock())
                    .setScale(2, BigDecimal.ROUND_HALF_UP);
            throw new BusinessException("库存不足，差异库存【" + diffStockQty.toPlainString() +"】");
        }
        return stock;
    }

    /**
     * <p>验证库存明细</p>
     *
     * @author whli
     * @since 2023/5/1 15:02
     * @param entity
     * @return
     */
    private StockDtlVO validStockDtlQty(StockDtlQueryDTO entity, BigDecimal stockQty) {
        List<StockDtlVO> stockDtls = stockDtlService.findByWarehouseAndProduct(entity);
        if (CollectionUtils.isEmpty(stockDtls)) {
            throw new BusinessException("库存不足，请补足后操作");
        }
        StockDtlVO stockDtl = stockDtls.get(0);
        if (stockQty.compareTo(stockDtl.getAvailableStock()) > 0) {
            BigDecimal diffStockQty = stockQty.subtract(stockDtl.getAvailableStock())
                    .setScale(2, BigDecimal.ROUND_HALF_UP);
            throw new BusinessException("库存不足，差异库存【" + diffStockQty.toPlainString() +"】");
        }
        return stockDtl;
    }

    /**
     * <p>验证库存占用</p>
     *
     * @author whli
     * @since 2023/5/1 15:01
     * @param entity
     * @return
     */
    private StockVO validOccupiedQty(StockQueryDTO entity, BigDecimal stockQty) {
        List<StockVO> stocks = stockService.findByWarehouse(entity);
        if (CollectionUtils.isEmpty(stocks)) {
            throw new BusinessException("库存占用不足，请补足后操作");
        }
        StockVO stock = stocks.get(0);
        if (stock.getOccupiedQty() == null || stockQty.compareTo(stock.getOccupiedQty()) > 0) {
            throw new BusinessException("库存占用不足，请补足后操作");
        }
        return stock;
    }

    /**
     * <p>验证库存明细占用</p>
     *
     * @author whli
     * @since 2023/5/1 15:02
     * @param entity
     * @return
     */
    private StockDtlVO validOccupiedDtlQty(StockDtlQueryDTO entity, BigDecimal stockQty) {
        List<StockDtlVO> stockDtls = stockDtlService.findByWarehouseAndProduct(entity);
        if (CollectionUtils.isEmpty(stockDtls)) {
            throw new BusinessException("库存占用不足，请补足后操作");
        }
        StockDtlVO stockDtl = stockDtls.get(0);
        if (stockDtl.getOccupiedQty() == null || stockQty.compareTo(stockDtl.getOccupiedQty()) > 0) {
            throw new BusinessException("库存占用不足，请补足后操作");
        }
        return stockDtl;
    }

    /**
     * <p>出库</p>
     *
     * @author whli
     * @since 2023/5/1 11:24
     * @param entity 参数
     * @return
     */
    private synchronized int outStock(StockActionDTO entity, boolean isOccupied) {
        if (entity == null) {
            throw new BusinessException("出库数据不能为空");
        }

        //默认值
        if (StringUtils.isEmpty(entity.getBatchNo())) {
            entity.setBatchNo(DEFAULT_BATCH);
        }
        if (StringUtils.isEmpty(entity.getSnNo())) {
            entity.setSnNo(DEFAULT_BATCH);
        }
        entity.setActionType(StockActionEnum.OUT_STOCK.getType());

        //验证
        this.validParams(entity);

        BigDecimal stockQty = entity.getStockQty().multiply(BigDecimal.valueOf(-1));
        StockQueryDTO stockQuery = StockActionConvert.INSTANCE.outToQuery(entity);
        StockDtlQueryDTO stockDtlQuery = StockActionConvert.INSTANCE.outToDtlQuery(entity);

        //占用
        if (isOccupied) {
            //验证主库存/库存明细占用
            StockVO stock = this.validOccupiedQty(stockQuery, entity.getStockQty());
            StockDtlVO stockDtl = this.validOccupiedDtlQty(stockDtlQuery, entity.getStockQty());

            //修改主库存、库存明细占用
            stockService.changeOccupiedQty(stock.getId(), stockQty);
            stockDtlService.changeOccupiedQty(stockDtl.getId(), stockQty);
        }

        //验证主库存/库存明细
        StockVO stock = this.validStockQty(stockQuery, entity.getStockQty());
        StockDtlVO stockDtl = this.validStockDtlQty(stockDtlQuery, entity.getStockQty());

        //修改主库存、库存明细
        stockService.changeStockQty(stock.getId(), stockQty);
        stockDtlService.changeStockQty(stockDtl.getId(), stockQty);

        //插入库存日志
        this.createStockLog(entity);
        return 1;
    }

    /**
     * <p>验证参数</p>
     *
     * @author whli
     * @since 2023/5/1 10:49
     * @param entity    参数
     * @return
     */
    private void validParams(StockActionDTO entity) {
        ValidateUtils.validate(entity);
        if (StockActionEnum.IN_STOCK.getType().equals(entity.getActionType())) {
            if (StringUtils.isEmpty(entity.getToWarehouseInfoId())) {
                throw new BusinessException("目的仓库不能为空");
            }
            if (StringUtils.isEmpty(entity.getToWarehouseInfoCode())) {
                throw new BusinessException("目的仓库编码不能为空");
            }
            if (StringUtils.isEmpty(entity.getToWarehouseInfoName())) {
                throw new BusinessException("目的仓库名称不能为空");
            }
            if (StringUtils.isEmpty(entity.getToWarehouseAreaId())) {
                throw new BusinessException("目的库区不能为空");
            }
            if (StringUtils.isEmpty(entity.getToWarehouseAreaCode())) {
                throw new BusinessException("目的库区编码不能为空");
            }
            if (StringUtils.isEmpty(entity.getToWarehouseAreaName())) {
                throw new BusinessException("目的库区名称不能为空");
            }
            if (StringUtils.isEmpty(entity.getToWarehouseLocationId())) {
                throw new BusinessException("目的库位不能为空");
            }
            if (StringUtils.isEmpty(entity.getToWarehouseLocationCode())) {
                throw new BusinessException("目的库位编码不能为空");
            }
            if (StringUtils.isEmpty(entity.getToWarehouseLocationName())) {
                throw new BusinessException("目的库位名称不能为空");
            }
        } else if (StockActionEnum.OUT_STOCK.getType().equals(entity.getActionType())) {
            if (StringUtils.isEmpty(entity.getFromWarehouseInfoId())) {
                throw new BusinessException("来源仓库不能为空");
            }
            if (StringUtils.isEmpty(entity.getFromWarehouseInfoCode())) {
                throw new BusinessException("来源仓库编码不能为空");
            }
            if (StringUtils.isEmpty(entity.getFromWarehouseInfoName())) {
                throw new BusinessException("来源仓库名称不能为空");
            }
            if (StringUtils.isEmpty(entity.getFromWarehouseAreaId())) {
                throw new BusinessException("来源库区不能为空");
            }
            if (StringUtils.isEmpty(entity.getFromWarehouseAreaCode())) {
                throw new BusinessException("来源库区编码不能为空");
            }
            if (StringUtils.isEmpty(entity.getFromWarehouseAreaName())) {
                throw new BusinessException("来源库区名称不能为空");
            }
            if (StringUtils.isEmpty(entity.getFromWarehouseLocationId())) {
                throw new BusinessException("来源库位不能为空");
            }
            if (StringUtils.isEmpty(entity.getFromWarehouseLocationCode())) {
                throw new BusinessException("来源库位编码不能为空");
            }
            if (StringUtils.isEmpty(entity.getFromWarehouseLocationName())) {
                throw new BusinessException("来源库位名称不能为空");
            }
        }
    }

    /**
     * <p>创建库存</p>
     *
     * @author whli
     * @since 2023/5/1 12:00
     * @param entity 参数
     * @return
     */
    private String createStock(Stock entity) {
        if (StringUtils.isEmpty(entity.getWarehouseCategory())) {
            entity.setWarehouseCategory(DEFAULT_WAREHOUSE_CATEGORY);
        }
        if (StringUtils.isEmpty(entity.getWarehouseProperty())) {
            entity.setWarehouseProperty(QualityEnum.PASS.getCode());
        }
        entity.setOccupiedQty(BigDecimal.ZERO);
        return stockService.save(entity);
    }

    /**
     * <p>创建库存明细</p>
     *
     * @author whli
     * @since 2023/5/1 12:51
     * @param stockId 库存主键
     * @param entity  参数
     * @return
     */
    private String createStockDtl(String stockId, StockDtl entity) {
        entity.setWarehouseStockId(stockId);
        entity.setOccupiedQty(BigDecimal.ZERO);
        return stockDtlService.save(entity);
    }

    /**
     * <p>创建库存日志</p>
     *
     * @author whli
     * @since 2023/5/1 12:56
     * @param entity
     * @return
     */
    private void createStockLog(StockActionDTO entity) {
        StockLog stockLog = StockActionConvert.INSTANCE.actionToLogDomain(entity);
        stockLogService.save(stockLog);
    }

}
